using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.ComponentModel;
using System.Xml.Serialization;

namespace GraphAlgsPresentation.Core
{
    /// <summary>
    /// The class provides information the layout context, including all the vertices and all the edges.
    /// </summary>
	[Serializable]
    public class LayoutContext
    {
        private List<LCVertex> vertices = new List<LCVertex>();
        private List<LCEdge> edges = new List<LCEdge>();
        private Color backColor = Color.LavenderBlush;

        #region Properties

        /// <summary>
        /// Gets or sets the vertices.
        /// </summary>
        /// <value>The vertices.</value>
        [Browsable(false)]
        public List<LCVertex> Vertices
        {
            get { return vertices; }
            set { vertices = value; }
        }

        /// <summary>
        /// Gets or sets the edges.
        /// </summary>
        /// <value>The edges.</value>
        [Browsable(false)]
        public List<LCEdge> Edges
        {
            get { return edges; }
            set { edges = value; }
        }

        /// <summary>
        /// Gets or sets the total vertices.
        /// </summary>
        /// <value>The total vertices.</value>
        [XmlIgnore]
        [Category("Graph")]
        [DisplayName("Total vertices")]
        [Description("The number of vertices in the graph.")]
        public int TotalVertices
        {
            get { return Vertices.Count; }
            set { }
        }

        /// <summary>
        /// Gets or sets the total edges.
        /// </summary>
        /// <value>The total edges.</value>
        [XmlIgnore]
        [Category("Graph")]
        [DisplayName("Total edges")]
        [Description("The number of edges in the graph.")]
        public int TotalEdges
        {
            get { return Edges.Count; }
            set { }
        }

        /// <summary>
        /// Gets or sets the color of the background.
        /// </summary>
        /// <value>The color of the background.</value>
        [XmlIgnore]
        [Category("Graph")]
        [DisplayName("Background Color")]
        [Description("The background color of the graph.")]
        public Color BackColor
        {
            get { return backColor; }
            set { backColor = value; }
        }

        /// <summary>
        /// Gets or sets the color of the XML background.
        /// </summary>
        /// <value>The color of the XML background.</value>
        [Browsable(false)]
        public string XMLBackColor
        {
            get { return XmlDataConverter.SerializeColor(BackColor); }
            set { BackColor = XmlDataConverter.DeserializeColor(value); }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Draws on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void Draw(Graphics g)
        {
            g.Clear(BackColor);
            foreach (LCEdge edge in Edges)
                edge.Draw(g);
            foreach (LCVertex vertex in Vertices)
                vertex.Draw(g);
            foreach (LCEdge edge in Edges)
                if (edge.Marked) edge.DrawMark(g);
            foreach (LCVertex vertex in Vertices)
                if (vertex.Marked) vertex.DrawMark(g);
        }

        /// <summary>
        /// Gets the next name of the a vertex.
        /// </summary>
        /// <returns></returns>
        public string GetNextName()
        {
            int max = 0;
            foreach (LCVertex vertex in Vertices)
            {
                try
                {
                    int id = Convert.ToInt32(vertex.Name);
                    if (id > max) max = id;
                }
                catch
                {
                }
            }
            max++;
            return max.ToString();
        }

        /// <summary>
        /// Gets the object from point (x, y).
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        public Object GetObject(int x, int y)
        {
            double eps = 1;
            foreach (LCVertex vertex in Vertices)
            {
                if (vertex.CheckInside(x, y)) return vertex;
            }
            foreach (LCEdge edge in Edges)
            {
                int sx = edge.Source.Position.X;
                int sy = edge.Source.Position.Y;
                int dx = edge.Destination.Position.X;
                int dy = edge.Destination.Position.Y;
                if (Math.Sqrt((x - sx) * (x - sx) + (y - sy) * (y - sy)) +
                    Math.Sqrt((x - dx) * (x - dx) + (y - dy) * (y - dy)) -
                    Math.Sqrt((sx - dx) * (sx - dx) + (sy - dy) * (sy - dy)) < eps)
                {
                    return edge;
                }
            }
            return null;
        }

        /// <summary>
        /// Gets the vertex.
        /// </summary>
        /// <param name="VertexName">Name of the vertex.</param>
        /// <returns></returns>
        public LCVertex GetVertex(String VertexName)
        {
            foreach (LCVertex vertex in Vertices)
                if (vertex.Name == VertexName) return vertex;
            return null;
        }

        /// <summary>
        /// Gets the edge.
        /// </summary>
        /// <param name="VertexName1">The name of the first vertex.</param>
        /// <param name="VertexName2">The name of the second vertex.</param>
        /// <returns></returns>
        public LCEdge GetEdge(String VertexName1, String VertexName2)
        {
            foreach (LCEdge edge in Edges)
            {
                if (edge.Directed)
                {
                    if ((edge.Source.Name == VertexName1) && (edge.Destination.Name == VertexName2))
                        return edge;
                }
                else
                {
                    if (((edge.Source.Name == VertexName1) && (edge.Destination.Name == VertexName2)) ||
                        ((edge.Destination.Name == VertexName1) && (edge.Source.Name == VertexName2)))
                        return edge;
                }
            }
            return null;
        }

        /// <summary>
        /// Refreshes this instance.
        /// </summary>
        public void Refresh()
        {
            foreach (LCEdge edge in Edges)
            {
                edge.Source = GetVertex(edge.SourceName);
                edge.Destination = GetVertex(edge.DestinationName);
            }
        }

        #endregion

        #region Serialization

        /// <summary>
        /// Serializes the specified output.
        /// </summary>
        /// <param name="output">The output.</param>
        public void Serialize(TextWriter output)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(LayoutContext),
            new Type[] { typeof(LCVertex), typeof(LCEdge) });
            serializer.Serialize(output, this);
        }

        /// <summary>
        /// Deserializes the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static LayoutContext Deserialize(TextReader input)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(LayoutContext),
                new Type[] { typeof(LCVertex), typeof(LCEdge) });
            LayoutContext result = serializer.Deserialize(input) as LayoutContext;
            result.Refresh();            
            return result;
        }

        #endregion
    }
}
